﻿namespace StockSharp.Algo.Indicators.Misc
{
	using System.Linq;

	/// <summary>
	/// Линейная регрессия - Value возвращает прогноз последней точки.
	/// </summary>
	public class LinearReg : LengthIndicator<decimal>
	{
		// Коэффициент при независимой переменной, угол наклона прямой.
		private decimal _slope;

		/// <summary>
		/// Создать <see cref="LinearReg"/>.
		/// </summary>
		public LinearReg()
			: base(typeof(decimal))
		{
		}

		/// <summary>
		/// Сбросить состояние индикатора на первоначальное. Метод вызывается каждый раз, когда меняются первоначальные настройки (например, длина периода).
		/// </summary>
		public override void Reset()
		{
			_slope = 0;
			base.Reset();
		}

		/// <summary>
		/// Обработать входное значение.
		/// </summary>
		/// <param name="input">Входное значение.</param>
		/// <returns>Результирующее значение.</returns>
		protected override decimal OnProcess(IIndicatorValue input)
		{
			var newValue = input.GetValue<decimal>();

			if (IsFormed)
			{
				// удаляем хвостовое значение
				Buffer.RemoveAt(0);
			}

			// добавляем новое начало
			Buffer.Add(newValue);

			if (IsFormed)
			{
				//x - независимая переменная, номер значения в буфере
				//y - зависимая переменная - значения из буфера
				decimal sumX = 0m; //сумма x
				decimal sumY = 0m; //сумма y
				decimal sumXy = 0m; //сумма x*y
				decimal sumX2 = 0m; //сумма x^2

				for (int i = 0; i < Buffer.Count; i++)
				{
					sumX += i;
					sumY += Buffer.ElementAt(i);
					sumXy += i * Buffer.ElementAt(i);
					sumX2 += i * i;
				}

				//коэффициент при независимой переменной
				var divisor = (Length * sumX2 - sumX * sumX);
				if (divisor == 0) _slope = 0;
				else _slope = (Length * sumXy - sumX * sumY) / divisor;

				//свободный член
				var b = (sumY - _slope * sumX) / Length;

				//прогноз последнего значения (его номер Length - 1)
				return _slope * (Length - 1) + b;
			}

			return this.GetCurrentValue();
		}
	}
}